package com.gauravk.audiovisualizer.visualizer;

import com.gauravk.audiovisualizer.base.BaseVisualizer;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.Rect;
import ohos.app.Context;

import org.jetbrains.annotations.Nullable;

/**
 * 圆形
 *
 * @author maple on 2019/4/24 15:17.
 * @version v1.0
 */
public class CircleLineVisualizer extends BaseVisualizer implements Component.DrawTask, ComponentContainer.ArrangeListener {
    private static final int BAR_MAX_POINTS = 240;
    private static final int BAR_MIN_POINTS = 30;
    private Rect mClipBounds;
    private int mPoints;
    private int mPointRadius;
    private float[] mSrcY;
    private int mRadius;
    private Paint mGPaint;
    private boolean drawLine;

    /**
     * 使用java代码实例化调用
     *
     * @param context
     */
    public CircleLineVisualizer(Context context) {
        super(context);
    }

    /**
     * 使用xml布局引用调用
     *
     * @param context
     * @param attrs
     */
    public CircleLineVisualizer(Context context, @Nullable AttrSet attrs) {
        super(context, attrs);
    }

    public boolean isDrawLine() {
        return drawLine;
    }

    /**
     * control the display of drawLine
     *
     * @param drawLine is show drawLine
     */
    public void setDrawLine(boolean drawLine) {
        this.drawLine = drawLine;
    }

    @Override
    protected void init() {
        addDrawTask(this::onDraw);
        setArrangeListener(this::onArrange);
        mPoints = (int) (BAR_MAX_POINTS * mDensity);
        if (mPoints < BAR_MIN_POINTS) {
            mPoints = BAR_MIN_POINTS;
        }
        mSrcY = new float[mPoints];
        mClipBounds = new Rect();
        setAnimationSpeed(mAnimSpeed);
        mPaint.setAntiAlias(true);
        mGPaint = new Paint();
        mGPaint.setAntiAlias(true);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        canvas.getLocalClipBounds(mClipBounds);
        updateData();

        // draw circle's points
        for (int i = 0; i < 360; i = i + 360 / mPoints) {
            float cx = (float) (getWidth() / 2 + Math.cos(i * Math.PI / 180) * mRadius);
            float cy = (float) (getHeight() / 2 - Math.sin(i * Math.PI / 180) * mRadius);
            canvas.drawCircle(cx, cy, mPointRadius, mPaint);
        }

        // draw lines
        if (drawLine) {
            drawLines(canvas);
        }

        // draw bar
        for (int i = 0; i < 360; i = i + 360 / mPoints) {
            if (mSrcY[i * mPoints / 360] == 0) {
                continue;
            }
            canvas.save();
            canvas.rotate(-i, getWidth() / 2, getHeight() / 2);
            float cx = (float) (getWidth() / 2 + mRadius);
            float cy = (float) (getHeight() / 2);
            canvas.drawRect(cx, cy - mPointRadius, cx + mSrcY[i * mPoints / 360],
                    cy + mPointRadius, mPaint);
            canvas.drawCircle(cx + mSrcY[i * mPoints / 360], cy, mPointRadius, mPaint);
            canvas.restore();
        }
        super.addDrawTask(this::onDraw);
    }

    /**
     * Draw a translucent ray
     *
     * @param canvas target canvas
     */
    private void drawLines(Canvas canvas) {
        int lineLen = 14 * mPointRadius;

        // default len,
        for (int i = 0; i < 360; i = i + 360 / mPoints) {
            canvas.save();
            canvas.rotate(-i, getWidth() / 2, getHeight() / 2);
            float cx = (float) (getWidth() / 2 + mRadius) + mSrcY[i * mPoints / 360];
            float cy = (float) (getHeight() / 2);
            Path path = new Path();
            path.moveTo(cx, cy + mPointRadius);
            path.lineTo(cx, cy - mPointRadius);
            path.lineTo(cx + lineLen, cy);
            canvas.drawPath(path, mGPaint);
            canvas.restore();
        }
    }

    private void updateData() {
        if (isVisualizationEnabled && mRawAudioBytes != null) {
            if (mRawAudioBytes.length == 0) {
                return;
            }
            for (int i = 0; i < mSrcY.length; i++) {
                int x = (int) Math.ceil((i + 1) * (mRawAudioBytes.length / mPoints));
                int t = 0;
                if (x < 1024) {
                    t = ((byte) (Math.abs(mRawAudioBytes[x]) + 128)) * mRadius / 128;
                }
                mSrcY[i] = -t;
            }
        }
    }

    /**
     * 大小发生变化调用
     * @param w    0
     * @param h    0
     * @param oldw 1176
     * @param oldh 1800
     * @return boolean
     */
    @Override
    public boolean onArrange(int w, int h, int oldw, int oldh) {
        setArrangeListener(this::onArrange);
        mRadius = Math.min(oldw, oldh) / 4;
        mPointRadius = Math.abs((int) (2 * mRadius * Math.sin(Math.PI / mPoints / 3)));
        Point[] newPoints = new Point[]{new Point(getWidth() / 2 + mRadius, getHeight() / 2)};
        float[] newStops = {getWidth() / 2 + mRadius + mPointRadius * 5, getHeight() / 2};
        Color[] newColors = new Color[]{new Color(Color.getIntColor("#77FF5722")), new Color(Color.getIntColor("#10FF5722"))};
        LinearShader lg = new LinearShader(
                newPoints,
                newStops,
                newColors,
                Shader.TileMode.CLAMP_TILEMODE);
        mGPaint.setShader(lg, Paint.ShaderType.PIXELMAP_SHADER);
        return false;
    }

    public void setVisualizationEnabled(boolean b) {
        isVisualizationEnabled = b;
    }
}